﻿using DirectShowLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace PlayCap.Wpf
{

    /// <summary>
    /// 工具类：DirectShow的
    /// </summary>
    public static class DirectShowUtility
    {

        #region Functions.Resolutions.002

        /// <summary>
        /// 获取摄像头所支持的分辨率
        /// </summary>
        /// <param name="filter"></param>
        public static List<Size> GetCaptureResolutions(IBaseFilter filter)
        {
            ICaptureGraphBuilder2 _builder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            return GetCaptureResolutions(_builder, filter);
        }

        /// <summary>
        /// 获取摄像头所支持的分辨率
        /// </summary>
        /// <param name="filter"></param>
        public static Dictionary<string, Size> GetCaptureResolutionDict(IBaseFilter filter)
        {
            ICaptureGraphBuilder2 _builder = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();
            return GetCaptureResolutionDict(_builder, filter);
        }

        #endregion

        #region Functions.Resolutions.001

        /// <summary>
        /// 获取摄像头所支持的分辨率
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="filter"></param>
        public static List<Size> GetCaptureResolutions(ICaptureGraphBuilder2 builder, IBaseFilter filter)
        {
            var _list = new List<Size>();
            // 获取配置接口
            int hr = builder.FindInterface(PinCategory.Capture,
                                            MediaType.Video,
                                            filter,
                                            typeof(IAMStreamConfig).GUID,
                                            out object streamConfig);

            DsError.ThrowExceptionForHR(hr);

            if (!(streamConfig is IAMStreamConfig videoStreamConfig))
            {
                throw new Exception("Failed to get IAMStreamConfig");
            }

            VideoStreamConfigCaps vscc = new VideoStreamConfigCaps();
            hr = videoStreamConfig.GetNumberOfCapabilities(out int iCount, out int iSize);
            DsError.ThrowExceptionForHR(hr);

            // 判断为正确获取的信息
            if (Marshal.SizeOf(vscc) == iSize)
            {
                for (int i = 0; i < iCount; ++i)
                {
                    // 分配非托管内存
                    IntPtr pVscc = Marshal.AllocCoTaskMem(Marshal.SizeOf(vscc));

                    hr = videoStreamConfig.GetStreamCaps(i, out AMMediaType amMediaType, pVscc);
                    DsError.ThrowExceptionForHR(hr);
                    // 如果为视频信息
                    if (amMediaType.majorType == MediaType.Video &&
                        amMediaType.formatType == FormatType.VideoInfo)
                    {
                        Marshal.StructureToPtr(vscc, pVscc, false);


                        var videoInfoHeader = new VideoInfoHeader();
                        Marshal.PtrToStructure(amMediaType.formatPtr, videoInfoHeader);


                        // 获取摄像头所支持的分辨率
                        int width = videoInfoHeader.BmiHeader.Width;
                        int height = videoInfoHeader.BmiHeader.Height;
                        _list.Add(new Size(width, height));
                    }
                    // 释放非托管内存
                    Marshal.FreeCoTaskMem(pVscc);
                    DsUtils.FreeAMMediaType(amMediaType);
                }
            }
            return _list;
        }

        /// <summary>
        /// 获取摄像头所支持的分辨率
        /// </summary>
        /// <param name="builder"></param>
        /// <param name="filter"></param>
        public static Dictionary<string, Size> GetCaptureResolutionDict(ICaptureGraphBuilder2 builder, IBaseFilter filter)
        {
            var _list = GetCaptureResolutions(builder, filter);
            var _dict = new Dictionary<string, Size>();
            if (_list != null)
            {
                var _newlist = _list.Distinct();
                foreach (var x in _newlist)
                {
                    _dict.Add($"{x.Width}x{x.Height}", x);
                }
            }
            return _dict;
        }

        #endregion

        #region Functions.Device.Video

        // This version of FindCaptureDevice is provide for education only.
        // A second version using the DsDevice helper class is define later.
        public static IBaseFilter FindFirstCaptureDevice()
        {
            int hr = 0;
#if USING_NET11
      UCOMIEnumMoniker classEnum = null;
      UCOMIMoniker[] moniker = new UCOMIMoniker[1];
#else
            IEnumMoniker classEnum = null;
            IMoniker[] moniker = new IMoniker[1];
#endif
            object source = null;

            // Create the system device enumerator
            ICreateDevEnum devEnum = (ICreateDevEnum)new CreateDevEnum();

            // Create an enumerator for the video capture devices
            hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, out classEnum, 0);
            DsError.ThrowExceptionForHR(hr);

            // The device enumerator is no more needed
            Marshal.ReleaseComObject(devEnum);

            // If there are no enumerators for the requested type, then 
            // CreateClassEnumerator will succeed, but classEnum will be NULL.
            if (classEnum == null)
            {
                throw new ApplicationException("No video capture device was detected.\r\n\r\n" +
                                               "This sample requires a video capture device, such as a USB WebCam,\r\n" +
                                               "to be installed and working properly.  The sample will now close.");
            }

            // Use the first video capture device on the device list.
            // Note that if the Next() call succeeds but there are no monikers,
            // it will return 1 (S_FALSE) (which is not a failure).  Therefore, we
            // check that the return code is 0 (S_OK).
#if USING_NET11
      int i;
      if (classEnum.Next (moniker.Length, moniker, IntPtr.Zero) == 0)
#else
            if (classEnum.Next(moniker.Length, moniker, IntPtr.Zero) == 0)
#endif
            {
                // Bind Moniker to a filter object
                Guid iid = typeof(IBaseFilter).GUID;
                moniker[0].BindToObject(null, null, ref iid, out source);
            }
            else
            {
                throw new ApplicationException("Unable to access video capture device!");
            }

            // Release COM objects
            Marshal.ReleaseComObject(moniker[0]);
            Marshal.ReleaseComObject(classEnum);

            // An exception is thrown if cast fail
            return (IBaseFilter)source;
        }



        //获取所有视频设备名称
        public static ArrayList GetVideoInputDevice()
        { return GetDeviceCollection(FilterCategory.VideoInputDevice); }
        private static ArrayList GetDeviceCollection(Guid DeviceType)
        {
            ArrayList returnString = new ArrayList();
            foreach (DsDevice ds in DsDevice.GetDevicesOfCat(DeviceType))
            {
                returnString.Add(ds.Name);
            }
            return returnString;
        }
        #endregion

    }

}
